//
//  LogManager.cpp
//  TRKUTIL
//
//  Created by 李通洲 on 15/1/24.
//  Copyright (c) 2015年 Wicresoft. All rights reserved.
//

#define _CRT_SECURE_NO_WARNINGS 1

#include <cstdlib>

#include "Helper.hpp"
#include "LogManager.hpp"

static void write_raw(fmt::BufferedFile& file, fmt::StringRef str) {
    std::fwrite(str.c_str(), str.size(), 1, file.get());
}

static void write_raw(fmt::BufferedFile& file, char c) {
    std::putc(c, file.get());
}

LogManager *LogManager::instance = NULL;

LogManager::LogManager(fmt::StringRef fileName, std::time_t t) :
    _t(t), _fileName(fileName), GlobalLogLevel(Unknown) {}

LogManager::~LogManager() {
    if (__flog.get()) {
        write_raw(__flog, '\n');
    }
}

fmt::BufferedFile& LogManager::getLogFile() {
    if (!__flog.get()) {
        __flog = fmt::BufferedFile(_fileName, "a");
        
        tm *now = std::localtime(&_t);
        __flog.print("{}:{}:{}\n", now->tm_hour, now->tm_min, now->tm_sec);
    }
    return __flog;
}

LogManager& LogManager::getInstance() {
    if (!instance) {
        fmt::MemoryWriter logFileName;
        logFileName << GetCurrentExecutablePath();
        logFileName << "logs/";

        if (!CreateDirectoryIfNonexist(logFileName.c_str())) {
            fmt::print(stderr, "Failed to create directory '{}', logs will NOT be saved\n");
        }

        time_t t = time(NULL);   // get time now
        tm *now = std::localtime(&t);
        logFileName.write("TRKUTIL_{}-{}-{}.log", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday);

        
        instance = new LogManager(logFileName.c_str(), t);
        
        std::atexit(Destruct);
    }
    
    return *instance;
}

void LogManager::WriteLog(fmt::StringRef msg, LogLevel level) {
    if (level >= Warning) {
        std::fputs(msg.c_str(), stderr);
        std::putchar('\n');
    }
    if (level < GlobalLogLevel) {
        return;
    }
    
    fmt::BufferedFile& flog = getLogFile();
    switch (level) {
        case Info:
            flog.get();
            write_raw(flog, "[INFO] ");
            break;
        case Warning:
            write_raw(flog, "[WARN] ");
            break;
        case Error:
            write_raw(flog, "[ERROR] ");
            break;
        default:
            break;
    }
    write_raw(flog, msg);
    write_raw(flog, '\n');
    if (level == Error) {
        std::fflush(flog.get());
    }
}

#ifdef _MSC_VER
_declspec(noreturn)
#else
__attribute__((__noreturn__))
#endif
void panic(fmt::StringRef msg, int errorCode) {
    LogManager::getInstance().LogError(msg);
    std::exit(errorCode);
}
